package com.gmlive.recyclerview.gridpager.indicator

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import androidx.recyclerview.widget.RecyclerView
import com.gmlive.recyclerview.gridpager.R

/**
 * @Author svenj
 * @Date 2021/1/22
 * @Email svenjzm@gmail.com
 */
class CirclePageIndicator @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : BasePageIndicator(context, attrs, defStyleAttr) {

    private val mPaintPageFill = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mPaintStroke = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mPaintFill = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mOrientation = 0
    private var mCentered = false
    private var mSnap = false
    private var mRadius = 0f
    private var mMargin = 0f

    init {
        //Load defaults from resources
        val res = resources
        val defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color)
        val defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color)
        val defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation)
        val defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color)
        val defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width)
        val defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius)
        val defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered)
        val defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap)

        //Retrieve styles attributes
        val a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyleAttr, 0)

        mCentered = a.getBoolean(R.styleable.CirclePageIndicator_cpi_centered, defaultCentered)
        mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation)
        mPaintPageFill.style = Paint.Style.FILL
        mPaintPageFill.color = a.getColor(R.styleable.CirclePageIndicator_cpi_pageColor, defaultPageColor)
        mPaintStroke.style = Paint.Style.STROKE
        mPaintStroke.color = a.getColor(R.styleable.CirclePageIndicator_cpi_strokeColor, defaultStrokeColor)
        mPaintStroke.strokeWidth = a.getDimension(R.styleable.CirclePageIndicator_cpi_strokeWidth, defaultStrokeWidth)
        mPaintFill.style = Paint.Style.FILL
        mPaintFill.color = a.getColor(R.styleable.CirclePageIndicator_cpi_fillColor, defaultFillColor)
        mRadius = a.getDimension(R.styleable.CirclePageIndicator_cpi_radius, defaultRadius)
        mMargin = a.getDimension(R.styleable.CirclePageIndicator_cpi_margin, 0F)
        //Default margin
        if (mMargin <= 0F) {
            mMargin = 3 * mRadius
        }
        mSnap = a.getBoolean(R.styleable.CirclePageIndicator_cpi_snap, defaultSnap)

        val background = a.getDrawable(R.styleable.CirclePageIndicator_android_background)
        background?.let { setBackground(it) }

        a.recycle()
    }

    fun setCentered(centered: Boolean) {
        mCentered = centered
        invalidate()
    }

    fun isCentered(): Boolean {
        return mCentered
    }

    fun setPageColor(pageColor: Int) {
        mPaintPageFill.color = pageColor
        invalidate()
    }

    fun getPageColor(): Int {
        return mPaintPageFill.color
    }

    fun setFillColor(fillColor: Int) {
        mPaintFill.color = fillColor
        invalidate()
    }

    fun getFillColor(): Int {
        return mPaintFill.color
    }

    fun setOrientation(orientation: Int) {
        when (orientation) {
            RecyclerView.HORIZONTAL, RecyclerView.VERTICAL -> {
                mOrientation = orientation
                requestLayout()
            }
            else -> throw IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.")
        }
    }

    fun getOrientation(): Int {
        return mOrientation
    }

    fun setStrokeColor(strokeColor: Int) {
        mPaintStroke.color = strokeColor
        invalidate()
    }

    fun getStrokeColor(): Int {
        return mPaintStroke.color
    }

    fun setStrokeWidth(strokeWidth: Float) {
        mPaintStroke.strokeWidth = strokeWidth
        invalidate()
    }

    fun getStrokeWidth(): Float {
        return mPaintStroke.strokeWidth
    }

    fun setRadius(radius: Float) {
        mRadius = radius
        invalidate()
    }

    fun getRadius(): Float {
        return mRadius
    }

    fun setMargin(margin: Float) {
        mMargin = margin
        invalidate()
    }

    fun getMargin(): Float {
        return mMargin
    }

    fun setSnap(snap: Boolean) {
        mSnap = snap
        invalidate()
    }

    fun isSnap(): Boolean {
        return mSnap
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (mRecyclerView == null) {
            return
        }
        val count = pageCount()
        if (count == 0) {
            return
        }
        if (mCurrentPage >= count) {
            setCurrentItem(count - 1)
            return
        }
        val longSize: Int
        val longPaddingBefore: Int
        val longPaddingAfter: Int
        val shortPaddingBefore: Int
        if (mOrientation == RecyclerView.HORIZONTAL) {
            longSize = width
            longPaddingBefore = paddingLeft
            longPaddingAfter = paddingRight
            shortPaddingBefore = paddingTop
        } else {
            longSize = height
            longPaddingBefore = paddingTop
            longPaddingAfter = paddingBottom
            shortPaddingBefore = paddingLeft
        }
        val shortOffset = shortPaddingBefore + mRadius
        var longOffset = longPaddingBefore + mRadius
        if (mCentered) {
            longOffset += (longSize - longPaddingBefore - longPaddingAfter) / 2.0f - count * mMargin / 2.0f
        }
        var dX: Float
        var dY: Float
        var pageFillRadius = mRadius
        if (mPaintStroke.strokeWidth > 0) {
            pageFillRadius -= mPaintStroke.strokeWidth / 2.0f
        }

        //Draw stroked circles
        for (iLoop in 0 until count) {
            val drawLong = longOffset + iLoop * mMargin
            if (mOrientation == RecyclerView.HORIZONTAL) {
                dX = drawLong
                dY = shortOffset
            } else {
                dX = shortOffset
                dY = drawLong
            }
            // Only paint fill if not completely transparent
            if (mPaintPageFill.alpha > 0) {
                canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill)
            }

            // Only paint stroke if a stroke width was non-zero
            if (pageFillRadius != mRadius) {
                canvas.drawCircle(dX, dY, mRadius, mPaintStroke)
            }
        }

        //Draw the filled circle according to the current scroll
        val cx = mCurrentPage * mMargin
        if (mOrientation == RecyclerView.HORIZONTAL) {
            dX = longOffset + cx
            dY = shortOffset
        } else {
            dX = shortOffset
            dY = longOffset + cx
        }
        canvas.drawCircle(dX, dY, mRadius, mPaintFill)
    }

    /*
     * (non-Javadoc)
     *
     * @see android.view.View#onMeasure(int, int)
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        if (mOrientation == RecyclerView.HORIZONTAL) {
            setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec))
        } else {
            setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec))
        }
    }

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private fun measureLong(measureSpec: Int): Int {
        var result: Int
        val specMode = MeasureSpec.getMode(measureSpec)
        val specSize = MeasureSpec.getSize(measureSpec)
        if (specMode == MeasureSpec.EXACTLY || mRecyclerView == null) {
            //We were told how big to be
            result = specSize
        } else {
            //Calculate the width according the views count
            val count = pageCount()
            result = (paddingLeft + paddingRight
                    + count * 2 * mRadius + (count - 1) * mRadius + 1).toInt()
            //Respect AT_MOST value if that was what is called for by measureSpec
            if (specMode == MeasureSpec.AT_MOST) {
                result = result.coerceAtMost(specSize)
            }
        }
        return result
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private fun measureShort(measureSpec: Int): Int {
        var result: Int
        val specMode = MeasureSpec.getMode(measureSpec)
        val specSize = MeasureSpec.getSize(measureSpec)
        if (specMode == MeasureSpec.EXACTLY) {
            //We were told how big to be
            result = specSize
        } else {
            //Measure the height
            result = (2 * mRadius + paddingTop + paddingBottom + 1).toInt()
            //Respect AT_MOST value if that was what is called for by measureSpec
            if (specMode == MeasureSpec.AT_MOST) {
                result = result.coerceAtMost(specSize)
            }
        }
        return result
    }
}